﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.ServiceModel.Description;
using System.IO;
using System.Xml.Linq;
using System.Security;
using System.Runtime.InteropServices;
using System.DirectoryServices.AccountManagement;
using Microsoft.Crm.Sdk.Samples;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Services.Utility;

namespace QueueManager.Helper.Crm
{
  public class CustomCrmServiceHelper : ServerConnection
  {
    //* Overides  microsofts class *//
    public override Configuration GetServerConfiguration()
    {
      // Read the configuration from the disk, if it exists, at C:\Users\<username>\AppData\Roaming\CrmServer\Credentials.xml.
      var isConfigExist = ReadConfigurations();

      // Check if server configuration settings are already available on the disk.
      // Get the existing user's logon credentials.
      config.Credentials = GetStoredUserLogonCredentials();

      // Set IServiceManagement for the current organization.
      var orgServiceManagement = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(config.OrganizationUri);
      config.OrganizationServiceManagement = orgServiceManagement;

      // Set SecurityTokenResponse for the current organization.
      if (config.EndpointType != AuthenticationProviderType.ActiveDirectory)
      {
        // Set the credentials.
        var authCredentials = new AuthenticationCredentials();

        // If UserPrincipalName exists, use it. Otherwise, set the logon credentials from the configuration.
        if (!String.IsNullOrWhiteSpace(config.UserPrincipalName))
        {
          authCredentials.UserPrincipalName = config.UserPrincipalName;
        }
        else
        {
          authCredentials.ClientCredentials = config.Credentials;
          if (config.EndpointType == AuthenticationProviderType.LiveId)
          {
            authCredentials.SupportingCredentials = new AuthenticationCredentials();
            authCredentials.SupportingCredentials.ClientCredentials = config.DeviceCredentials;
          }
        }
        var tokenCredentials = orgServiceManagement.Authenticate(authCredentials);
        if (tokenCredentials != null)
        {
          if (tokenCredentials.SecurityTokenResponse != null)
            config.OrganizationTokenResponse = tokenCredentials.SecurityTokenResponse;
        }
      }

      return config;

    }

    // * Extensions to the base class *//
    public void SetServerConfiguration(string serverAddress, bool useSecureSocketLayer, bool office365)
    {
      if (configurations == null)
        configurations = new List<Configuration>();

      if (configurations.Count == 0)
        configurations.Add(new Configuration());

      if (serverAddress.EndsWith(".dynamics.com") || String.IsNullOrWhiteSpace(serverAddress))
        useSecureSocketLayer = true;

      config.ServerAddress = serverAddress;

      // One of the Microsoft Dynamics CRM Online data centers.
      if (config.ServerAddress.EndsWith(".dynamics.com", StringComparison.InvariantCultureIgnoreCase))
      {
        // Check if the organization is provisioned in Microsoft Office 365.
        if (office365)
        {
          config.DiscoveryUri = new Uri(String.Format("https://disco.{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));
        }
        else
        {
          config.DiscoveryUri = new Uri(String.Format("https://dev.{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));

          // Get or set the device credentials. This is required for Windows Live ID authentication. 
          config.DeviceCredentials = GetDeviceCredentials();
        }
      }
      // Check if the server uses Secure Socket Layer (https).
      else if (useSecureSocketLayer)
        config.DiscoveryUri = new Uri(String.Format("https://{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));
      else
        config.DiscoveryUri = new Uri(String.Format("http://{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));

      //Set endpoint type
      config.EndpointType = GetEndpointType(config.DiscoveryUri);

    }
    private ClientCredentials GetStoredUserLogonCredentials()
    {
      var credentials = new ClientCredentials();
      var isCredentialExist = (config.Credentials != null);
      switch (config.EndpointType)
      {
        // An on-premises Microsoft Dynamics CRM server deployment. 
        case AuthenticationProviderType.ActiveDirectory:
          var userName = ConfigurationManager.AppSettings["Username"];
          var password = ConfigurationManager.AppSettings["Password"];
          var domain = ConfigurationManager.AppSettings["Domain"];
          credentials.Windows.ClientCredential = new System.Net.NetworkCredential(userName, password, domain);
          break;
        // A Microsoft Dynamics CRM Online server deployment. 
        case AuthenticationProviderType.LiveId:
          credentials.UserName.UserName = ConfigurationManager.AppSettings["Username"];
          ;
          credentials.UserName.Password = ConfigurationManager.AppSettings["Password"];
          ;
          break;
        // An internet-facing deployment (IFD) of Microsoft Dynamics CRM.          
        case AuthenticationProviderType.Federation:
          credentials.UserName.UserName = ConfigurationManager.AppSettings["Username"];
          credentials.UserName.Password = ConfigurationManager.AppSettings["Password"];
          break;
        // Managed Identity/Federated Identity users using Microsoft Office 365.
        case AuthenticationProviderType.OnlineFederation:
          if (!isCredentialExist && config.AuthFailureCount == 0)
          {
            // Initial try with the current UserPrincipalName for the Federated Identity organization.
            // else config.UserPrincipalName has value, and it will use the same existing UserPrincipalName.
            if (String.IsNullOrWhiteSpace(config.UserPrincipalName))
              config.UserPrincipalName = UserPrincipal.Current.UserPrincipalName;
            // using existing UserPrincipalName instead of UserName & Password.
            return null;
          }

          // Fetch/Request user credentials. 
          config.UserPrincipalName = String.Empty;
          credentials.UserName.UserName = ConfigurationManager.AppSettings["Username"];
          credentials.UserName.Password = ConfigurationManager.AppSettings["Password"];
          break;
        default:
          credentials = null;
          break;
      }
      return credentials;
    }
    private AuthenticationProviderType GetEndpointType(Uri discoveryUri)
    {
      var serviceManagement = ServiceConfigurationFactory.CreateManagement<IDiscoveryService>(discoveryUri);
      return serviceManagement.AuthenticationType;
    }

    public void SetUserLogonCredentials(string usernameInput, string password)
    {
      var credentials = new ClientCredentials();
      switch (config.EndpointType)
      {
        // An on-premises Microsoft Dynamics CRM server deployment. 
        case AuthenticationProviderType.ActiveDirectory:
          // Uses credentials from windows credential manager for earlier saved configuration.
          var domainAndUserName = usernameInput.Split('\\');
          var domain = domainAndUserName[0];
          var userName = domainAndUserName[1];

          if (null != password)
            credentials.Windows.ClientCredential = new System.Net.NetworkCredential(userName, password, domain);
          else
            credentials.Windows.ClientCredential = null;

          break;
        // A Microsoft Dynamics CRM Online server deployment. 
        case AuthenticationProviderType.LiveId:
          if (string.IsNullOrWhiteSpace(usernameInput))
            return;

          credentials.UserName.UserName = usernameInput;
          credentials.UserName.Password = password;
          break;

        // An internet-facing deployment (IFD) of Microsoft Dynamics CRM.          
        case AuthenticationProviderType.Federation:
          if (string.IsNullOrWhiteSpace(usernameInput))
            return;

          credentials.UserName.UserName = usernameInput;
          credentials.UserName.Password = password;
          break;

        // Managed Identity/Federated Identity users using Microsoft Office 365.
        case AuthenticationProviderType.OnlineFederation:
          // Use saved credentials.
          credentials.UserName.UserName = usernameInput;
          credentials.UserName.Password = password;
          break;
        default:
          credentials = null;
          break;
      }
      config.Credentials = credentials;
    }
    public OrganizationDetailCollection GetOrganizations()
    {
      using (var serviceProxy = GetDiscoveryProxy())
      {
        // Obtain organization information from the Discovery service. 
        if (serviceProxy != null)
        {
          // Obtain information about the organizations that the system user belongs to.
          return DiscoverOrganizations(serviceProxy);
        }
      }
      return null;
    }

    /// <summary>
    /// Reads a server configuration file.
    /// Read the configuration from disk, if it exists, at C:\Users\YourUserName\AppData\Roaming\CrmServer\Credentials.xml.
    /// </summary>
    /// <returns>Is configuration settings already available on disk.</returns>
    public Boolean ReadConfigurations()
    {
      var isConfigExist = false;

      //Read config from app.config instead
      config.ServerAddress = ConfigurationManager.AppSettings["ServerAddress"];
      config.OrganizationName = ConfigurationManager.AppSettings["OrganizationName"];
      if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["DiscoveryUri"]))
        config.DiscoveryUri = new Uri(ConfigurationManager.AppSettings["DiscoveryUri"]);
      if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["OrganizationUri"]))
        config.OrganizationUri = new Uri(ConfigurationManager.AppSettings["OrganizationUri"]);
      if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["HomeRealmUri"]))
        config.HomeRealmUri = new Uri(ConfigurationManager.AppSettings["HomeRealmUri"]);
      config.EndpointType = RetrieveAuthenticationType(ConfigurationManager.AppSettings["EndpointType"]);
      config.Credentials = ParseInCredentials(ConfigurationManager.AppSettings["Username"], ConfigurationManager.AppSettings["Domain"], config.EndpointType);
      if (config.EndpointType == AuthenticationProviderType.LiveId)
        config.DeviceCredentials = GetDeviceCredentials();
      config.UserPrincipalName = ConfigurationManager.AppSettings["UserPrincipalName"];

      ////End
      //if (File.Exists(CrmServiceHelperConstants.ServerCredentialsFile))
      //{
      //  XElement configurationsFromFile = XElement.Load(CrmServiceHelperConstants.ServerCredentialsFile);
      //  foreach (XElement config in configurationsFromFile.Nodes())
      //  {
      //    Configuration newConfig = new Configuration();
      //    var serverAddress = config.Element("ServerAddress");
      //    if (serverAddress != null)
      //      if (!String.IsNullOrEmpty(serverAddress.Value))
      //        newConfig.ServerAddress = serverAddress.Value;
      //    var organizationName = config.Element("OrganizationName");
      //    if (organizationName != null)
      //      if (!String.IsNullOrEmpty(organizationName.Value))
      //        newConfig.OrganizationName = organizationName.Value;
      //    var discoveryUri = config.Element("DiscoveryUri");
      //    if (discoveryUri != null)
      //      if (!String.IsNullOrEmpty(discoveryUri.Value))
      //        newConfig.DiscoveryUri = new Uri(discoveryUri.Value);
      //    var organizationUri = config.Element("OrganizationUri");
      //    if (organizationUri != null)
      //      if (!String.IsNullOrEmpty(organizationUri.Value))
      //        newConfig.OrganizationUri = new Uri(organizationUri.Value);
      //    var homeRealmUri = config.Element("HomeRealmUri");
      //    if (homeRealmUri != null)
      //      if (!String.IsNullOrEmpty(homeRealmUri.Value))
      //        newConfig.HomeRealmUri = new Uri(homeRealmUri.Value);

      //    var vendpointType = config.Element("EndpointType");
      //    if (vendpointType != null)
      //      newConfig.EndpointType =
      //              RetrieveAuthenticationType(vendpointType.Value);
      //    if (config.Element("Credentials").HasElements)
      //    {
      //      newConfig.Credentials =
      //          ParseInCredentials(config.Element("Credentials"), newConfig.EndpointType);
      //    }
      //    if (newConfig.EndpointType == AuthenticationProviderType.LiveId)
      //    {
      //      newConfig.DeviceCredentials = GetDeviceCredentials();
      //    }
      //    var userPrincipalName = config.Element("UserPrincipalName");
      //    if (userPrincipalName != null)
      //      if (!String.IsNullOrWhiteSpace(userPrincipalName.Value))
      //        newConfig.UserPrincipalName = userPrincipalName.Value;
      //    configurations.Add(newConfig);
      //  }
      //}

      return true;
    }

    /// <summary>
    /// Parse credentials from an XML node to required ClientCredentials data type 
    /// based on passed AuthenticationProviderType.
    /// </summary>
    /// <param name="credentials">Credential XML node.</param>
    /// <param name="endpointType">AuthenticationProviderType of the credential.</param>
    /// <returns>Required ClientCredentials type.</returns>
    private ClientCredentials ParseInCredentials(string userName, string domain, AuthenticationProviderType endpointType)
    {
      var result = new ClientCredentials();

      switch (endpointType)
      {
        case AuthenticationProviderType.ActiveDirectory:
          result.Windows.ClientCredential = new System.Net.NetworkCredential()
          {
            UserName = userName,
            Domain = domain
          };
          break;
        case AuthenticationProviderType.LiveId:
        case AuthenticationProviderType.Federation:
        case AuthenticationProviderType.OnlineFederation:
          result.UserName.UserName = userName;
          break;
        default:
          break;
      }

      return result;
    }

  }
}
